package com.zongtui.fourinone.park;

import com.zongtui.fourinone.base.BeanService;
import com.zongtui.fourinone.base.config.ConfigContext;
import com.zongtui.fourinone.exception.LeaderException;
import com.zongtui.fourinone.obj.ObjValue;
import com.zongtui.fourinone.park.utils.AsyncExecutor;
import com.zongtui.fourinone.utils.log.LogUtil;

import java.rmi.ConnectException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 领导节点.
 */
public class ParkLeader {
    //是否是主节点：默认不是.
    boolean isMaster = false;
    //是否想成为领导节点：默认不是
    boolean alwaysTry = false;
    //节点服务名.
    private String parkServiceCfg = "ParkService";
    //当前服务.
    private String[] thisServer;
    //默认群组服务
    String[][] groupServer = new String[][]{{"localhost", "1888"}, {"localhost", "1889"}, {"localhost", "1890"}};
    private LinkedBlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
    private AsyncExecutor rpl = null;

    ParkLeader(String host, int port, String parkServiceCfg) {
        this.parkServiceCfg = parkServiceCfg;
        thisServer = new String[]{host, "" + port};
        this.alwaysTry = Boolean.valueOf(ConfigContext.getConfig("park", "alwaysTryLeader", null, "false"));
    }

    ParkLeader(String host, int port, String[][] groupServer, String parkServiceCfg) {
        this(host, port, parkServiceCfg);
        this.groupServer = groupServer;
    }

    protected Park getLeaderPark() {
        LogUtil.info("", "", "getLeaderPark...................");
        int index = getLeaderIndex(thisServer);
        return electionLeader(-1, index);
    }

    protected Park getNextLeader() {
        LogUtil.info("", "", "getNextLeader...................");
        int index = getLeaderIndex(thisServer);
        return electionLeader(index, index + 1);
    }

    private int getLeaderIndex(String[] sa) {
        int i = 0;
        for (; i < groupServer.length; i++)
            if (Arrays.equals(sa, groupServer[i]))
                break;
        return i;
    }

    protected Park electionLeader(int b, int i) {
        Park pk = null;
        boolean thesarrok = true;
        i = i < groupServer.length ? i : 0;
        //b=b<0?groupServer.length-1:b;
        String[] sarr = groupServer[i];
        try {
            pk = (Park) BeanService.getBean(sarr[0], Integer.parseInt(sarr[1]), parkServiceCfg);
            if (pk != null)
                pk.askLeader();
        } catch (RemoteException re) {
            LogUtil.info("electionLeader", "(" + sarr[0] + ":" + sarr[1] + "):", re.getMessage());
            thesarrok = false;
            if (re instanceof ConnectException) {
                if (b != i)//one cycle
                {
                    b = !alwaysTry && b < 0 ? i : b;
                    pk = electionLeader(b, i + 1);
                }
            }
        } catch (LeaderException le) {
            //le.printStackTrace();
            LogUtil.info("[electionLeader]", "[LeaderException]", le.getMessage());
            thesarrok = false;
            String[] ls = le.getLeaderServer();
            int leaderindex = getLeaderIndex(ls);
            pk = electionLeader(-1, leaderindex);
        }
        if (thesarrok) {
            thisServer = sarr;
            LogUtil.info("", "", "leader server is(" + thisServer[0] + ":" + thisServer[1] + ")");
        }
        return pk;
    }

    protected Park electionLeader(int i) {
        Park pk = null;
        boolean thesarrok = true;
        i = i < groupServer.length ? i : 0;
        String[] sarr = groupServer[i];
        try {
            pk = (Park) BeanService.getBean(sarr[0], Integer.parseInt(sarr[1]), parkServiceCfg);
            if (pk != null)
                pk.askLeader();
        } catch (RemoteException re) {
            LogUtil.info("electionLeader", "(" + sarr[0] + ":" + sarr[1] + "):", re.getMessage());
            thesarrok = false;
            if (re instanceof ConnectException) {
                pk = electionLeader(i + 1);
            }
        } catch (LeaderException le) {
            //le.printStackTrace();
            LogUtil.info("electionLeader", "LeaderException", le);
            thesarrok = false;
            String[] ls = le.getLeaderServer();
            int leaderindex = getLeaderIndex(ls);
            pk = electionLeader(leaderindex);
        }
        if (thesarrok) {
            thisServer = sarr;
            LogUtil.info("", "", "leader server is(" + thisServer[0] + ":" + thisServer[1] + ")");
        }
        return pk;
    }

    protected Park[] getOtherPark() {
        ArrayList<Park> pklist = new ArrayList<>();
        for (String[] sarr : groupServer) {
            if (!Arrays.equals(thisServer, sarr)) {
                try {
                    Park pk = (Park) BeanService.getBean(sarr[0], Integer.parseInt(sarr[1]), parkServiceCfg);//try catch cant null
                    pklist.add(pk);
                } catch (RemoteException re) {
                    LogUtil.fine("getOtherPark", "(" + sarr[0] + ":" + sarr[1] + "):", re.getMessage());
//                    re.printStackTrace();
                }
            }
        }
        return pklist.toArray(new Park[pklist.size()]);
    }

    protected boolean checkMasterPark(String[] sv, Park pk) {
        if (isMaster || getOtherMasterPark(sv) == null) {//cant isMaster for double conflict in net break
            copyArray(thisServer, sv);
            setMaster(true, pk);
            return true;
        } else return false;
    }

    protected void wantBeMaster(Park pk) {
        LogUtil.info("", "", "wantBeMaster.............................");
        String[] sv = new String[2];
        Park othermaster = getOtherMasterPark(sv);
        if (othermaster == null) {
            LogUtil.info("", "", "get one of other parks for init parkInfo.........");
            Park[] pks = getOtherPark();
            if (pks.length > 0)
                setInitParkInfo(pks[0], pk);
            setMaster(true, pk);
        } else {
            LogUtil.info("", "", "wantBeMaster,master is (" + sv[0] + ":" + sv[1] + ")");
            setInitParkInfo(othermaster, pk);
        }
    }

    private void setInitParkInfo(Park fromPk, Park toPk) {
        try {
            toPk.setParkinfo(fromPk.getParkinfo());
        } catch (Exception re) {
            //re.printStackTrace();
            LogUtil.info("[ParkLeader]", "[setInitParkInfo]", re);
        }
    }

    private void setMaster(boolean ismaster, Park pk) {
        this.isMaster = ismaster;
        LogUtil.info("", "", "setMaster(" + thisServer[0] + ":" + thisServer[1] + "):" + ismaster);
        if (this.isMaster)
            HbDaemo.runClearTask((ParkService) pk);
    }

    protected String[] isMaster() {
        return isMaster ? thisServer : null;
    }

    protected Park getOtherMasterPark(String[] sv) {
        Park pkmaster = null;
        try {
            Park[] pks = getOtherPark();
            for (Park pk : pks) {
                String[] ask = pk.askMaster();
                if (ask != null) {
                    pkmaster = pk;
                    copyArray(ask, sv);
                }
            }
        } catch (Exception re) {
            //re.printStackTrace();
            LogUtil.info("getOtherMasterPark", "exception", re);
        }
        return pkmaster;
    }

    protected void runCopyTask(String domainnodekey, final Park pk) {
        LogUtil.fine("", "", "runCopyTask:" + domainnodekey + "............................");

        try {
            blockingQueue.put(domainnodekey);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }

        //static{}
        if (rpl == null) {
            LogUtil.fine("", "", "runCopyTask AsyncExector:");
            (rpl = new AsyncExecutor() {
                public void task() {
                    try {
                        while (true) {
                            String curkey = blockingQueue.take();
                            LogUtil.fine("", "", "runCopyTask blockingQueue.size():" + blockingQueue.size());
                            if (blockingQueue.size() == 0) {
                                LogUtil.fine("", "", "curkey:" + curkey);
                                //Thread.sleep(1000);
                                copyParkinfo(pk.getParkinfo());
                            }
                        }
                    } catch (Exception e) {
                        //e.printStackTrace();
                        LogUtil.info("runCopyTask", "exception", e);
                    }
                }
            }).run();
        }
    }

    private Boolean[] copyParkinfo(ObjValue pov) {
        ArrayList<Boolean> sendlist = new ArrayList<>();
        try {
            Park[] pks = getOtherPark();
            for (Park pk : pks)
                sendlist.add(pk.setParkinfo(pov));
        } catch (Exception re) {
            LogUtil.info("copyParkinfo", "exception", re);
        }
        return sendlist.toArray(new Boolean[sendlist.size()]);
    }

    private void copyArray(String[] fromArr, String[] toArr) {
        System.arraycopy(fromArr, 0, toArr, 0, toArr.length);
    }

    public String[] getThisServer() {
        return thisServer;
    }

    public static void main(String[] args) {
        ParkLeader pl = new ParkLeader("localhost", 1888, "ParkService");
        String[] sv = new String[2];
        System.out.println(pl.getOtherMasterPark(sv));
        System.out.println(sv[1]);
    }
}